﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.IO;
using System.Diagnostics;
using System.Windows.Interop;

namespace KeyboardImageViewer
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private bool fullScreen = true;
        private bool overlayInfobar = true;

        private ImageManager manager;

        private InputMapping inputMapping;
        private bool allowInput = true;

        private ViewerImage currentImage;

        private const int WM_KEYDOWN = 0x100;
        private const int WM_SYSKEYDOWN = 0x0104;

        private IntPtr hwnd;

        public MainWindow()
        {
            InitializeComponent();

            ComponentDispatcher.ThreadPreprocessMessage += new ThreadMessageEventHandler(ComponentDispatcher_ThreadPreprocessMessage);

            this.manager = new ImageManager(this);

            this.inputMapping = new InputMapping(this.manager.Connection);

            FullscreenStart fullscreenStart = this.manager.Config.FullscreenStartSetting;
            switch (fullscreenStart)
            {
                case FullscreenStart.RememberLast:
                    if (this.manager.Config.FullscreenStart)
                    {
                        this.SetFullscreen();
                    }
                    else
                    {
                        this.SetWindowed();
                    }

                    break;
                case FullscreenStart.AlwaysFullscreen:
                    this.SetFullscreen();
                    break;
                case FullscreenStart.AlwaysWindowed:
                    this.SetWindowed();
                    break;
                default:
                    break;
            }

            if (!this.manager.Config.OverlayInfobar)
            {
                this.OverlayInfobar = false;
            }
        }

        public void SetPicture(CachedImage image)
        {
            if (this.manager == null)
            {
                return;
            }

            this.loadingText.Visibility = Visibility.Collapsed;

            if (image != null && image.Image != null)
            {
                ViewerImage viewerImage = image.Image;

                if (viewerImage.IsAnimated)
                {
                    try
                    {
                        this.gifImage.GIFSource = Database.GetImageName(this.manager.Connection, viewerImage.ImageId);
                    }
                    catch (FileNotFoundException)
                    {
                        viewerImage.FileExists = false;
                    }

                    this.backgroundRectangle.Visibility = Visibility.Hidden;
                    this.imageGrid.Visibility = Visibility.Hidden;
                    this.gifImage.Visibility = Visibility.Visible;
                }
                else
                {
                    this.gifImage.GIFSource = null;
                    this.gifImage.Visibility = Visibility.Hidden;
                    this.backgroundRectangle.Visibility = Visibility.Visible;
                    this.imageGrid.Visibility = Visibility.Visible;

                    this.imageGrid.Children.Remove(image.UIImage);
                    this.imageGrid.Children.Remove(this.backgroundRectangle);
                    this.imageGrid.Children.Add(this.backgroundRectangle);

                    if (!viewerImage.Corrupted && viewerImage.FileExists)
                    {
                        this.imageGrid.Children.Add(image.UIImage);
                        this.backgroundRectangle.Fill = viewerImage.BgColor;
                    }
                    else
                    {
                        this.backgroundRectangle.Fill = Brushes.Black;
                    }
                }

                this.currentImage = image.Image;
            }
            else
            {
                this.gifImage.Visibility = Visibility.Hidden;
                this.backgroundRectangle.Visibility = Visibility.Hidden;
                this.imageGrid.Visibility = Visibility.Hidden;

                this.gifImage.GIFSource = null;

                this.currentImage = null;
            }

            this.UpdateInfoBar(this.manager);
        }

        public void SetLoadingPicture()
        {
            this.imageGrid.Children.Remove(this.backgroundRectangle);
            this.imageGrid.Children.Add(this.backgroundRectangle);
            this.loadingText.Visibility = Visibility.Visible;
            this.backgroundRectangle.Fill = Brushes.Black;

            this.UpdateInfoBarUserData(this.manager.CurrentImageData);
        }

        public Image AddToCache(ViewerImage viewerImage)
        {
            if (!viewerImage.FileExists || viewerImage.IsAnimated || viewerImage.Corrupted)
            {
                return null;
            }

            Image uiImage = new Image();
            //RenderOptions.SetBitmapScalingMode(uiImage, BitmapScalingMode.LowQuality);
            uiImage.Source = viewerImage.Image;

            this.imageGrid.Children.Insert(0, uiImage);

            return uiImage;
        }

        public void RemoveFromCache(Image uiImage)
        {
            if (uiImage != null)
            {
                this.imageGrid.Children.Remove(uiImage);
            }
        }

        public void ClearCache()
        {
            this.imageGrid.Children.RemoveRange(0, this.imageGrid.Children.Count);
        }

        public bool AllowInput
        {
            get { return this.allowInput; }
            set { this.allowInput = value; }
        }

        public void UpdateInfoBar(ImageManager manager)
        {
            this.infoBarGrid.Visibility = Visibility.Visible;

            if (manager.ImageShowing && this.currentImage != null)
            {
                ImageUserData imageData = manager.CurrentImageData;

                if (imageData.Tags.Count > 0)
                {
                    this.tags.Visibility = Visibility.Visible;
                    this.tags.Text = "Tags: " + imageData.TagsString;
                }
                else
                {
                    this.tags.Visibility = Visibility.Collapsed;
                }

                this.fileName.Text = this.manager.Config.SourceDirectories.GetRelativeName(imageData.Name);

                if (!this.currentImage.FileExists)
                {
                    this.fileNoLongerExists.Visibility = Visibility.Visible;
                }
                else
                {
                    this.fileNoLongerExists.Visibility = Visibility.Collapsed;
                }

                if (this.currentImage.Corrupted)
                {
                    this.corrupted.Visibility = Visibility.Visible;
                }
                else
                {
                    this.corrupted.Visibility = Visibility.Collapsed;
                }

                if (manager.Config.TrackDuplicates && this.currentImage.Duplicates != null)
                {
                    if (this.currentImage.Duplicates.Count > 0)
                    {
                        if (this.currentImage.Duplicates[0].ExactDuplicate)
                        {
                            this.duplicate.Foreground = Brushes.Blue;
                            this.duplicate.Text = "(Duplicate File)";
                            this.duplicate.Visibility = Visibility.Visible;
                        }
                        else
                        {
                            this.duplicate.Foreground = Brushes.Aqua;
                            this.duplicate.Text = "(Duplicate Image)";
                            this.duplicate.Visibility = Visibility.Visible;
                        }
                    }
                    else
                    {
                        this.duplicate.Visibility = Visibility.Collapsed;
                    }
                }
                else
                {
                    this.duplicate.Visibility = Visibility.Collapsed;
                }

                if (imageData.Rating > 0)
                {
                    this.rating.Visibility = Visibility.Visible;
                    this.rating.Text = imageData.Rating.ToString();

                    this.rating.Foreground = Utilities.GetColorFromRating(imageData.Rating);
                }
                else
                {
                    this.rating.Visibility = Visibility.Collapsed;
                }

                this.viewIndex.Text = (manager.CurrentIndex + 1) + "/" + manager.TotalImages;
            }
            else
            {
                this.fileNoLongerExists.Visibility = Visibility.Collapsed;
                this.corrupted.Visibility = Visibility.Collapsed;

                this.fileName.Text = "";
                this.rating.Text = "";

                this.tags.Text = "";
                this.duplicate.Text = "";
                this.viewIndex.Text = "0/0";
            }

            this.UpdateImageQueryInfo(manager.ImageQuery);
        }

        private void UpdateInfoBarUserData(ImageUserData imageData)
        {
            if (imageData.Tags.Count > 0)
            {
                this.tags.Visibility = Visibility.Visible;
                this.tags.Text = "Tags: " + imageData.TagsString;
            }
            else
            {
                this.tags.Visibility = Visibility.Collapsed;
            }

            this.fileName.Text = this.manager.Config.SourceDirectories.GetRelativeName(imageData.Name);

            if (imageData.Tags.Count > 0)
            {
                this.tags.Visibility = Visibility.Visible;
                this.tags.Text = "Tags: " + imageData.TagsString;
            }
            else
            {
                this.tags.Visibility = Visibility.Collapsed;
            }

            this.fileName.Text = this.manager.Config.SourceDirectories.GetRelativeName(imageData.Name);

            this.fileNoLongerExists.Visibility = Visibility.Collapsed;
            this.corrupted.Visibility = Visibility.Collapsed;
            this.duplicate.Visibility = Visibility.Collapsed;

            if (imageData.Rating > 0)
            {
                this.rating.Visibility = Visibility.Visible;
                this.rating.Text = imageData.Rating.ToString();

                this.rating.Foreground = Utilities.GetColorFromRating(imageData.Rating);
            }
            else
            {
                this.rating.Visibility = Visibility.Collapsed;
            }

            this.viewIndex.Text = (manager.CurrentIndex + 1) + "/" + manager.TotalImages;

            this.UpdateImageQueryInfo(manager.ImageQuery);
        }

        private void UpdateImageQueryInfo(ImageQuery imageQuery)
        {
            if (imageQuery.ListName != "" && manager.CurrentListId != manager.Config.StartingListId)
            {
                this.modifiers.Text = "List: " + imageQuery.ListName;
            }
            else
            {
                int ratingView = imageQuery.MinRating;
                if (ratingView == -1)
                {
                    this.modifiers.Text = "Unrated, ";
                }
                else if (ratingView > 0)
                {
                    this.modifiers.Text = ratingView.ToString() + "+, ";
                }
                else
                {
                    this.modifiers.Text = "";
                }

                if (imageQuery.UntaggedOnly)
                {
                    this.modifiers.Text += "Untagged, ";
                }

                if (imageQuery.Search != "")
                {
                    this.modifiers.Text += "\"" + imageQuery.Search + "\", ";
                }

                if (imageQuery.CustomClause != "")
                {
                    this.modifiers.Text += "Custom Clause, ";
                }

                if (imageQuery.Sort == SortType.Modified)
                {
                    this.modifiers.Text += "Last Modified";

                    if (imageQuery.Ascending)
                    {
                        this.modifiers.Text += ": Oldest First";
                    }
                }
                else if (imageQuery.Sort == SortType.Random)
                {
                    this.modifiers.Text += "Random";
                }
                else if (imageQuery.Sort == SortType.Name)
                {
                    this.modifiers.Text += "File Name";

                    if (!imageQuery.Ascending)
                    {
                        this.modifiers.Text += ": Reverse Order";
                    }
                }
            }
        }

        public bool ProgressBarVisible
        {
            get
            {
                return this.indexingProgressPanel.Visibility == Visibility.Visible;
            }
            set
            {
                if (value)
                {
                    this.gifImage.Visibility = Visibility.Hidden;
                    this.infoBarGrid.Visibility = Visibility.Collapsed;
                    this.indexProgress.Value = 0;
                    this.indexingProgressPanel.Visibility = Visibility.Visible;
                }
                else
                {
                    this.gifImage.Visibility = Visibility.Visible;
                    this.indexingProgressPanel.Visibility = Visibility.Collapsed;
                }
            }
        }

        public void ReportProgress(double progress)
        {
            this.indexProgress.Value = progress;
        }

        public bool OverlayInfobar
        {
            set
            {
                if (this.overlayInfobar != value)
                {
                    this.overlayInfobar = value;

                    if (value)
                    {
                        Grid.SetRowSpan(this.imageGrid, 2);
                        Grid.SetRowSpan(this.gifImage, 2);
                        this.overlayBorder.Visibility = Visibility.Visible;
                    }
                    else
                    {
                        Grid.SetRowSpan(this.imageGrid, 1);
                        Grid.SetRowSpan(this.gifImage, 1);
                        this.overlayBorder.Visibility = Visibility.Collapsed;
                    }
                }
            }
        }

        public bool FullScreen
        {
            get { return this.fullScreen; }
        }

        public double ImageAreaAspectRatio
        {
            get
            {
                return this.imageGrid.ActualWidth / this.imageGrid.ActualHeight;
            }
        }

        private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            bool control = Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl);
            bool alt = Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt);

            if (control)
            {
                this.ExecuteUserAction(this.inputMapping.GetActionFromKeyCtrl(e.Key));
            }
            else
            {
                this.ExecuteUserAction(this.inputMapping.GetActionFromKey(e.Key));
            }
        }

        private void ExecuteUserAction(UserAction action)
        {
            if (!this.allowInput && action != UserAction.ToggleFullscreen && action != UserAction.Quit)
            {
                return;
            }

            switch (action)
            {
                case UserAction.None:
                    break;
                case UserAction.ShowHelp:
                    EditKeys editKeys = new EditKeys(this.manager);
                    editKeys.Owner = this;
                    if (editKeys.ShowDialog() == true)
                    {
                        this.inputMapping = new InputMapping(this.manager.Connection);
                    }
                    break;
                case UserAction.ShowOptions:
                    Settings settingsWindow = new Settings(this.manager, this);
                    if (settingsWindow.ShowDialog() == true)
                    {
                        this.manager.UpdateSettings(settingsWindow);
                    }
                    break;
                case UserAction.ReloadFilesFromDisk:
                    this.manager.ReloadFiles();
                    break;
                case UserAction.ToggleFullscreen:
                    this.ToggleFullscreen();
                    break;
                case UserAction.Minimize:
                    this.WindowState = WindowState.Minimized;
                    break;
                case UserAction.Quit:
                    this.Close();
                    break;
                case UserAction.RateAs1:
                    this.manager.RateImage(1);
                    break;
                case UserAction.RateAs2:
                    this.manager.RateImage(2);
                    break;
                case UserAction.RateAs3:
                    this.manager.RateImage(3);
                    break;
                case UserAction.RateAs4:
                    this.manager.RateImage(4);
                    break;
                case UserAction.RateAs5:
                    this.manager.RateImage(5);
                    break;
                case UserAction.ClearRating:
                    this.manager.RateImage(0);
                    break;
                case UserAction.Tag:
                    if (this.manager.ImageShowing)
                    {
                        EditTagBox tagBox = new EditTagBox(this.manager, this);
                        tagBox.ShowDialog();
                    }
                    break;
                case UserAction.TagEditMultiple:
                    if (this.manager.ImageShowing)
                    {
                        EditMultipleTags editMultipleTagBox = new EditMultipleTags(this.manager, this);
                        editMultipleTagBox.ShowDialog();
                    }
                    break;
                case UserAction.Rename:
                    this.ShowRename();
                    break;
                case UserAction.Move:
                    this.manager.MoveVisibleFiles();
                    this.Focus();
                    break;
                case UserAction.EditCurrentFile:
                    this.manager.EditCurrentImage();
                    break;
                case UserAction.DeleteCurrentFile:
                    this.manager.DeleteCurrentImage();
                    break;
                case UserAction.Import:
                    Import import = new Import(this.manager, this);
                    import.ShowDialog();
                    break;
                case UserAction.EditTagDependencies:
                    EditTagDependencies dependenciesForm = new EditTagDependencies(this.manager, this);
                    dependenciesForm.ShowDialog();
                    break;
                case UserAction.ShowPreviousImage:
                    this.manager.MoveLeft();
                    break;
                case UserAction.ShowNextImage:
                    this.manager.MoveRight();
                    break;
                case UserAction.MoveToFirstImage:
                    this.manager.MoveToStart();
                    break;
                case UserAction.MoveToLastImage:
                    this.manager.MoveToEnd();
                    break;
                case UserAction.ShowLists:
                    ViewLists viewLists = new ViewLists(this.manager);
                    viewLists.Owner = this;
                    viewLists.ShowDialog();
                    break;
                case UserAction.ChangeOrder:
                    this.manager.ChangeOrder();
                    break;
                case UserAction.Search:
                    this.ShowSearch();
                    break;
                case UserAction.ClearSearch:
                    this.manager.ClearSearch();
                    break;
                case UserAction.ShowRating1OrGreater:
                    this.manager.SetRatingView(1);
                    break;
                case UserAction.ShowRating2OrGreater:
                    this.manager.SetRatingView(2);
                    break;
                case UserAction.ShowRating3OrGreater:
                    this.manager.SetRatingView(3);
                    break;
                case UserAction.ShowRating4OrGreater:
                    this.manager.SetRatingView(4);
                    break;
                case UserAction.ShowRating5OrGreater:
                    this.manager.SetRatingView(5);
                    break;
                case UserAction.ClearRatingFilter:
                    this.manager.SetRatingView(0);
                    break;
                case UserAction.ShowOnlyUnrated:
                    this.manager.SetRatingView(-1);
                    break;
                case UserAction.ShowOnlyUntagged:
                    this.manager.ToggleUntagged();
                    break;
                case UserAction.ShowDuplicates:
                    if (this.manager.Config.TrackDuplicates && this.manager.ImageShowing && this.currentImage != null && this.currentImage.Duplicates != null && this.currentImage.Duplicates.Count > 0)
                    {
                        Duplicates duplicates = new Duplicates(this.manager, this.currentImage.Duplicates);
                        duplicates.Owner = this;
                        duplicates.ShowDialog();
                        this.manager.RemoveDeletedImageData(duplicates.DeletedImages);
                    }
                    break;
                case UserAction.ShowAllDuplicates:
                    if (!this.manager.Config.TrackDuplicates)
                    {
                        MessageBox.Show("To show all duplicates, you have to enable duplicate tracking in Settings first.");
                    }
                    else
                    {
                        AllDuplicates duplicates = new AllDuplicates(this.manager);
                        duplicates.Owner = this;
                        duplicates.ShowDialog();
                        this.manager.RemoveDeletedImageData(duplicates.DeletedImages);
                    }
                    break;
                default:
                    Debug.Assert(false, "Unknown command encountered");
                    break;
            }
        }

        private void Window_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            int clicks = e.Delta / -120;

            if (clicks > 0)
            {
                this.ExecuteUserAction(this.inputMapping.GetActionFromMouseWheel(MouseWheelAction.Down));
            }
            else if (clicks < 0)
            {
                this.ExecuteUserAction(this.inputMapping.GetActionFromMouseWheel(MouseWheelAction.Up));
            }
        }

        private void Window_MouseDown(object sender, MouseButtonEventArgs e)
        {
            this.ExecuteUserAction(this.inputMapping.GetActionFromMouseButton(e.ChangedButton));
        }

        private void ToggleFullscreen()
        {
            if (this.fullScreen)
            {
                this.SetWindowed();
            }
            else
            {
                this.manager.SaveWindowSize();

                this.SetFullscreen();
            }
        }

        private void SetFullscreen()
        {
            this.fullScreen = true;

            this.Cursor = Cursors.None;
            this.WindowStyle = WindowStyle.None;
            this.ResizeMode = ResizeMode.NoResize;
            this.WindowState = WindowState.Maximized;
        }

        private void SetWindowed()
        {
            this.fullScreen = false;

            this.Cursor = Cursors.Arrow;
            this.WindowStyle = WindowStyle.SingleBorderWindow;
            this.ResizeMode = ResizeMode.CanResize;
            this.WindowState = WindowState.Normal;
        }

        private void ShowSearch()
        {
            SearchBox searchBox = new SearchBox(this.manager, this);
            searchBox.ShowDialog();
        }

        private void ShowRename()
        {
            if (this.manager.ImageShowing)
            {
                RenameBox renameBox = new RenameBox(this.manager, this);
                renameBox.ShowDialog();
            }
        }

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);
            this.hwnd = new WindowInteropHelper(this).Handle;
        }

        private void ComponentDispatcher_ThreadPreprocessMessage(ref MSG msg, ref bool handled)
        {
            if (msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN)
            {
                System.Windows.Forms.Keys keyData = ((System.Windows.Forms.Keys)((int)((long)msg.wParam)));
                if (((keyData & System.Windows.Forms.Keys.Enter) == System.Windows.Forms.Keys.Enter) &&
                    (Keyboard.Modifiers == ModifierKeys.Alt) &&
                    (msg.hwnd == this.hwnd))
                {
                    this.ToggleFullscreen();
                }
            }
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (this.WindowState == WindowState.Normal)
            {
                this.manager.SaveWindowSize();
            }

            if (this.manager.Config.FullscreenStartSetting == FullscreenStart.RememberLast)
            {
                this.manager.Config.FullscreenStart = this.fullScreen;
            }

            this.manager.PromptToApplyUpdate();
        }
    }
}
